﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Protocol;

namespace MDS
{
    public class MessageSerializer
    {
        class MessageType : EnumBase<byte>
        {
            private MessageType(byte value) : base(value) { }
            static MessageType()
            {
                BasicInfo = new MessageType(0);
                Bid = new MessageType(1);
                Tick = new MessageType(2);
                TradeSignal = new MessageType(3);
            }

            public static MessageType BasicInfo { get; private set; }
            public static MessageType Bid { get; private set; }
            public static MessageType Tick { get; private set; }
            public static MessageType TradeSignal { get; private set; }
        }
        public static byte[] Serialize(IMessage message)
        {


            if (message is GWMKTSETTradeSingalANS)
            {
                GWMKTSETTradeSingalANS signalMsg = message as GWMKTSETTradeSingalANS;
                return tradeSingalToBytes(signalMsg);
            }
            else if (message is GWMKTBidAskANS)
            {
                GWMKTBidAskANS bidMsg = message as GWMKTBidAskANS;
                return bidAskToBytes(bidMsg);
            }
            else if (message is GWMKTTicksANS)
            {
                GWMKTTicksANS tickMsg = message as GWMKTTicksANS;
                return tickToBytes(tickMsg);
            }
            else if (message is GWMKTBasicInfoANS)
            {
                GWMKTBasicInfoANS basicMsg = message as GWMKTBasicInfoANS;
                return basicToBytes(basicMsg);
            }
            else
            {
                throw new Exception("不支持的消息类型，只能序列化GWMKTSETTradeSingalANS、GWMKTBidAskANS、GWMKTTicksANS、GWMKTBasicInfoANS");
            }
        }

        public static bool TryDeserialize(byte[] data, out IMessage message)
        {
            message = null;
            if (data == null || data.Length == 0)
                return false;
            try
            {
                if (data[0] == MessageType.BasicInfo)
                {
                    message = bytesToBasic(data);
                }
                else if (data[0] == MessageType.Bid)
                {
                    message = bytesToBidAsk(data);
                }
                else if (data[0] == MessageType.Tick)
                {
                    message = bytesToTicks(data);
                }
                else if (data[0] == MessageType.TradeSignal)
                {
                    message = bytesToTradeSingal(data);
                }

            }
            catch (Exception ex)
            {
            }
            if (message == null)
                return false;
            else
                return true;
        }





        #region 序列化Message


        private static byte[] tickToBytes(Protocol.GWMKTTicksANS ticks)
        {
            byte[] rtn = new byte[156];
            rtn[0] = MessageType.Tick;

            int offset = 1;
            writeInt32(rtn, ref offset, ticks.SequenceNo);
            writeInt32(rtn, ref offset, ticks.Flag);
            writeChar(rtn, ref offset, ticks.ActionFlag);
            writeString(rtn, ref offset, ticks.StkID);
            writeInt64(rtn, ref offset, ticks.KnockTime);
            writeDouble(rtn, ref offset, ticks.HighPrice);
            writeDouble(rtn, ref offset, ticks.Lowprice);
            writeInt64(rtn, ref offset, ticks.MktKnockQty);
            writeDouble(rtn, ref offset, ticks.IOPV);
            writeDouble(rtn, ref offset, ticks.KnockAmt);
            writeDouble(rtn, ref offset, ticks.KnockPrice);
            writeInt64(rtn, ref offset, ticks.TotalBuyQty);
            writeDouble(rtn, ref offset, ticks.BuyAvgPrice);
            writeInt64(rtn, ref offset, ticks.TotalSellQty);
            writeDouble(rtn, ref offset, ticks.SellAvgPrice);
            writeInt32(rtn, ref offset, ticks.TradesCount);
            writeDouble(rtn, ref offset, ticks.TotalMktKnockAmt);
            writeInt64(rtn, ref offset, ticks.TotalMktKnockQty);
            writeInt64(rtn, ref offset, ticks.TotalBuyQtyDiff);
            writeInt64(rtn, ref offset, ticks.TotalSellQtyDiff);

            return rtn;
        }
        private static byte[] bidAskToBytes(Protocol.GWMKTBidAskANS bidAsk)
        {
            byte[] rtn = new byte[57];
            rtn[0] = MessageType.Bid;

            int offset = 1;
            writeInt32(rtn, ref offset, bidAsk.SequenceNo);
            writeInt32(rtn, ref offset, bidAsk.Flag);
            writeChar(rtn, ref offset, bidAsk.ActionFlag);
            writeString(rtn, ref offset, bidAsk.StkID);
            writeInt64(rtn, ref offset, bidAsk.OccurTime);
            writeDouble(rtn, ref offset, bidAsk.BuyPrice1);
            writeDouble(rtn, ref offset, bidAsk.SellPrice1);
            writeInt64(rtn, ref offset, bidAsk.BuyQty1);
            writeInt64(rtn, ref offset, bidAsk.SellQty1);

            return rtn;
        }
        private static byte[] tradeSingalToBytes(Protocol.GWMKTSETTradeSingalANS tradeSingal)
        {
            byte[] rtn = new byte[18];
            rtn[0] = MessageType.TradeSignal;

            int offset = 1;

            writeInt32(rtn, ref offset, tradeSingal.SequenceNo);
            writeInt32(rtn, ref offset, tradeSingal.Flag);
            writeChar(rtn, ref offset, tradeSingal.ActionFlag);
            writeInt64(rtn, ref offset, tradeSingal.TradeDay);

            return rtn;
        }
        private static byte[] basicToBytes(Protocol.GWMKTBasicInfoANS baisc)
        {
            byte[] rtn = new byte[256];
            rtn[0] = MessageType.BasicInfo;
            int offset = 1;
            writeInt32(rtn, ref offset, baisc.ContractSerialNo);
            writeInt64(rtn, ref offset, baisc.TradeDay);
            writeChar(rtn, ref offset, baisc.ExchID);
            writeString(rtn, ref offset, baisc.StkID);
            writeString(rtn, ref offset, baisc.StkName);
            writeString(rtn, ref offset, baisc.StkType);
            writeString(rtn, ref offset, baisc.TradeType);
            writeDouble(rtn, ref offset, baisc.MaxOrderPrice);
            writeDouble(rtn, ref offset, baisc.MinOrderPrice);
            writeDouble(rtn, ref offset, baisc.Closeprice);
            writeDouble(rtn, ref offset, baisc.OpenPrice);
            writeInt64(rtn, ref offset, baisc.TotalIssueQty);
            writeDouble(rtn, ref offset, baisc.AccuredInterest);
            writeInt32(rtn, ref offset, baisc.TradeUnit);
            writeDouble(rtn, ref offset, baisc.ParValue);
            writeInt64(rtn, ref offset, baisc.TotalCirculatingShare);
            writeInt64(rtn, ref offset, baisc.BeginDate);
            writeInt64(rtn, ref offset, baisc.EndDate);
            writeDouble(rtn, ref offset, baisc.OrderPriceUnit);
            writeInt32(rtn, ref offset, baisc.Decimal);
            writeChar(rtn, ref offset, baisc.TradeStatus);
            writeChar(rtn, ref offset, baisc.SuspendedFlag);
            writeInt32(rtn, ref offset, baisc.PreOpenPosition);
            writeInt32(rtn, ref offset, baisc.OpenPosition);
            writeInt64(rtn, ref offset, baisc.PrevTotalMktKnockQty);
            writeDouble(rtn, ref offset, baisc.PrevTotalMktKnockAmt);
            writeInt32(rtn, ref offset, baisc.LowBuyQtyLimit);
            writeInt32(rtn, ref offset, baisc.LowSellQtyLimit);
            writeInt32(rtn, ref offset, baisc.HighBuyQtyLimit);
            writeInt32(rtn, ref offset, baisc.HighSellQtyLimit);
            writeInt32(rtn, ref offset, baisc.convertQty);
            writeString(rtn, ref offset, baisc.MarketTime);
            return rtn;
        }



        private static void writeInt32(byte[] buff, ref int offset, Int32Field value)
        {
            if (value != null)
            {
                BitConverter.GetBytes(value.Value).CopyTo(buff, offset);
            }
            else
            {
                BitConverter.GetBytes((Int32)0).CopyTo(buff, offset);
            }
            offset += 4;
        }

        private static void writeChar(byte[] buff, ref int offset, CharField value)
        {
            if (value != null)
            {
                buff[offset] = (byte)value.Value;

            }
            else
            {
                buff[offset] = (byte)0;
            }

            offset += 1;
        }

        private static void writeString(byte[] buff, ref int offset, StringField value)
        {
            if (value != null)
            {
                System.Text.Encoding.Default.GetBytes(value.Value).CopyTo(buff, offset);
            }
            else
            {
                System.Text.Encoding.Default.GetBytes("0000000").CopyTo(buff, offset);
            }

            offset += 7;
        }

        private static void writeInt64(byte[] buff, ref int offset, Int64Field value)
        {
            if (value != null)
            {
                BitConverter.GetBytes(value.Value).CopyTo(buff, offset);
            }
            else
            {
                BitConverter.GetBytes((Int64)0).CopyTo(buff, offset);
            }
            offset += 8;
        }

        private static void writeDouble(byte[] buff, ref int offset, DoubleField value)
        {
            if (value != null)
            {
                BitConverter.GetBytes(value.Value).CopyTo(buff, offset);
            }
            else
            {
                BitConverter.GetBytes((Double)0).CopyTo(buff, offset);
            }
            offset += 8;
        }

        #endregion

        #region 反序列化Message




        private static GWMKTSETTradeSingalANS bytesToTradeSingal(byte[] buff)
        {
            GWMKTSETTradeSingalANS tradeSignal = new GWMKTSETTradeSingalANS();
            int offset = 1;
            tradeSignal.SequenceNo = readInt32(buff, ref offset);
            tradeSignal.Flag = readInt32(buff, ref offset);
            tradeSignal.ActionFlag = readChar(buff, ref offset);
            tradeSignal.TradeDay = readInt64(buff, ref offset);
            return tradeSignal;
        }

        private static GWMKTBidAskANS bytesToBidAsk(byte[] buff)
        {
            GWMKTBidAskANS bid = new GWMKTBidAskANS();
            int offset = 1;
            bid.SequenceNo = readInt32(buff, ref offset);
            bid.Flag = readInt32(buff, ref offset);
            bid.ActionFlag = readChar(buff, ref offset);
            bid.StkID = readString(buff, ref offset);
            bid.OccurTime = readInt64(buff, ref offset);
            bid.BuyPrice1 = readDouble(buff, ref offset);
            bid.SellPrice1 = readDouble(buff, ref offset);
            bid.BuyQty1 = readInt64(buff, ref offset);
            bid.SellQty1 = readInt64(buff, ref offset);

            return bid;
        }

        private static GWMKTTicksANS bytesToTicks(byte[] buff)
        {
            GWMKTTicksANS tick = new GWMKTTicksANS();
            int offset = 1;
            tick.SequenceNo = readInt32(buff, ref offset);
            tick.Flag = readInt32(buff, ref offset);
            tick.ActionFlag = readChar(buff, ref offset);
            tick.StkID = readString(buff, ref offset);
            tick.KnockTime = readInt64(buff, ref offset);
            tick.HighPrice = readDouble(buff, ref offset);
            tick.Lowprice = readDouble(buff, ref offset);
            tick.MktKnockQty = readInt64(buff, ref offset);
            tick.IOPV = readDouble(buff, ref offset);
            tick.KnockAmt = readDouble(buff, ref offset);
            tick.KnockPrice = readDouble(buff, ref offset);
            tick.TotalBuyQty = readInt64(buff, ref offset);
            tick.BuyAvgPrice = readDouble(buff, ref offset);
            tick.TotalSellQty = readInt64(buff, ref offset);
            tick.SellAvgPrice = readDouble(buff, ref offset);
            tick.TradesCount = readInt32(buff, ref offset);
            tick.TotalMktKnockAmt = readDouble(buff, ref offset);
            tick.TotalMktKnockQty = readInt64(buff, ref offset);
            tick.TotalBuyQtyDiff = readInt64(buff, ref offset);
            tick.TotalSellQtyDiff = readInt64(buff, ref offset);

            return tick;
        }


        private static GWMKTBasicInfoANS bytesToBasic(byte[] buff)
        {
            GWMKTBasicInfoANS basicInfo = new GWMKTBasicInfoANS();

            int offset = 1;

            basicInfo.ContractSerialNo = readInt32(buff, ref offset);
            basicInfo.TradeDay = readInt64(buff, ref offset);
            basicInfo.ExchID = readChar(buff, ref offset);
            basicInfo.StkID = readString(buff, ref offset);
            basicInfo.StkName = readString(buff, ref offset);
            basicInfo.StkType = readString(buff, ref offset);
            basicInfo.TradeType = readString(buff, ref offset);
            basicInfo.MaxOrderPrice = readDouble(buff, ref offset);
            basicInfo.MinOrderPrice = readDouble(buff, ref offset);
            basicInfo.Closeprice = readDouble(buff, ref offset);
            basicInfo.OpenPrice = readDouble(buff, ref offset);
            basicInfo.TotalIssueQty = readInt64(buff, ref offset);
            basicInfo.AccuredInterest = readDouble(buff, ref offset);
            basicInfo.TradeUnit = readInt32(buff, ref offset);
            basicInfo.ParValue = readDouble(buff, ref offset);
            basicInfo.TotalCirculatingShare = readInt64(buff, ref offset);
            basicInfo.BeginDate = readInt64(buff, ref offset);
            basicInfo.EndDate = readInt64(buff, ref offset);
            basicInfo.OrderPriceUnit = readDouble(buff, ref offset);
            basicInfo.Decimal = readInt32(buff, ref offset);
            basicInfo.TradeStatus = readChar(buff, ref offset);
            basicInfo.SuspendedFlag = readChar(buff, ref offset);
            basicInfo.PreOpenPosition = readInt32(buff, ref offset);
            basicInfo.OpenPosition = readInt32(buff, ref offset);
            basicInfo.PrevTotalMktKnockQty = readInt64(buff, ref offset);
            basicInfo.PrevTotalMktKnockAmt = readDouble(buff, ref offset);
            basicInfo.LowBuyQtyLimit = readInt32(buff, ref offset);
            basicInfo.LowSellQtyLimit = readInt32(buff, ref offset);
            basicInfo.HighBuyQtyLimit = readInt32(buff, ref offset);
            basicInfo.HighSellQtyLimit = readInt32(buff, ref offset);
            basicInfo.convertQty = readInt32(buff, ref offset);
            basicInfo.MarketTime = readString(buff, ref offset);

            return basicInfo;
        }


        private static Int32 readInt32(byte[] buff, ref int offset)
        {
            Int32 rtn = BitConverter.ToInt32(buff, offset);
            offset += 4;
            return rtn;
        }

        private static Int64 readInt64(byte[] buff, ref int offset)
        {
            Int64 rtn = BitConverter.ToInt64(buff, offset);
            offset += 8;
            return rtn;
        }

        private static Char readChar(byte[] buff, ref int offset)
        {
            Char rtn = (Char)buff[offset];
            offset += 1;
            return rtn;
        }

        private static Double readDouble(byte[] buff, ref int offset)
        {
            Double rtn = BitConverter.ToDouble(buff, offset);
            offset += 8;
            return rtn;
        }

        private static string readString(byte[] buff, ref int offset)
        {
            string str = System.Text.Encoding.Default.GetString(buff, offset, 7);
            offset += 7;
            return str;
        }

        #endregion
    }
}
